package cn.piesat.scanning.business.common.algorithm.dataClear;

import com.alibaba.fastjson.JSON;
import cn.piesat.scanning.business.common.AlgorithmDefineAbstract;
import cn.piesat.scanning.dto.*;
import cn.piesat.scanning.service.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 数据清理
 *
 * @author xhl
 */
@Component
public class DataClear extends AlgorithmDefineAbstract {

    private static final Logger logger = LoggerFactory.getLogger(DataClear.class);

    @Autowired
    private HtPathConfigurationService htPathConfigurationService;
    @Autowired
    private HtScheduleJobService htScheduleJobService;
    @Autowired
    private DbDmsSchedulerTaskFileRecordService dbDmsSchedulerTaskFileRecordService;
    @Autowired
    private HtScanningMetadataService htScanningMetadataService;
    @Autowired
    private DbDmsAlgorithmResultStoreService dbDmsAlgorithmResultStoreService;
    @Autowired
    private DbDmsDatafileNearlineStorageService dbDmsDatafileNearlineStorageService;

    @Override
    public void exec(Map<String, Object> params, String taskId) {
        DataClearParams dataClearParams = JSON.parseObject(JSON.toJSONString(params), DataClearParams.class);
        HtScheduleJobDTO taskDTO = htScheduleJobService.findById(taskId);
        HtPathConfigurationDTO pathDTO = htPathConfigurationService.findById(taskDTO.getPathConfigId());
        String path = pathDTO.getScanningPath();
        Boolean flag = dataClearParams.getDeleteDb();
        if (flag) {
            //根据数据库里的信息删除磁盘的文件
            deleteDbAndDistData(dataClearParams,taskId);
        } else {
            //只删除磁盘上的文件
            deleteDistFile(dataClearParams,path,taskId);
        }
    }

    /**
     * 删除数据库和磁盘的文件
     * @param dataClearParams 数据清理参数对象
     * @param taskId 任务ID
     */
    public void deleteDbAndDistData(DataClearParams dataClearParams, String taskId){
        String fileNamePattern = dataClearParams.getFileNamePattern();
        String delTime = dataClearParams.getDelTime();
        String table = dataClearParams.getTable();
        String[] splitTable = table.split(",");
        List<String> tableMarks = Arrays.stream(splitTable).collect(Collectors.toList());
        if (StringUtils.isBlank(fileNamePattern) || StringUtils.isBlank(delTime)) {
            return ;
        }
        long plus = Long.valueOf(delTime) * (-1);
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        String date = dtf.format(LocalDateTime.now().plusHours(plus));
        List<String> fileNameList = new ArrayList<>();
        List<String> pathIdList = new ArrayList<>();

        List<HtScanningMetadataDTO> metadataDTOList = null;
        if (tableMarks.contains("metadata")) {
            metadataDTOList = htScanningMetadataService.regexAndDateRangeFind(date, fileNamePattern);
            fileNameList.addAll(metadataDTOList.stream().map(HtScanningMetadataDTO::getFileName).collect(Collectors.toList()));
            pathIdList.addAll(metadataDTOList.stream().map(HtScanningMetadataDTO::getPathConfigId).collect(Collectors.toList()));
        }
        List<DbDmsAlgorithmResultStoreDTO> resultDataDTOList = null;
        if (tableMarks.contains("resultData")) {
            resultDataDTOList = dbDmsAlgorithmResultStoreService.regexAndDateRangeTime(date, fileNamePattern);
            fileNameList.addAll(resultDataDTOList.stream().map(DbDmsAlgorithmResultStoreDTO::getFilename).collect(Collectors.toList()));
            pathIdList.addAll(resultDataDTOList.stream().map(DbDmsAlgorithmResultStoreDTO::getPathConfigId).collect(Collectors.toList()));
        }
        List<DbDmsDatafileNearlineStorageDTO> nearlineDataDTOList = null;
        if (tableMarks.contains("nearLine")) {
            nearlineDataDTOList = dbDmsDatafileNearlineStorageService.regexAndDateRangeTime(date, fileNamePattern);
            fileNameList.addAll(nearlineDataDTOList.stream().map(DbDmsDatafileNearlineStorageDTO::getFileName).collect(Collectors.toList()));
            pathIdList.addAll(nearlineDataDTOList.stream().map(DbDmsDatafileNearlineStorageDTO::getPathConfigId).collect(Collectors.toList()));
        }
        List<HtPathConfigurationDTO> pathList = htPathConfigurationService.findByIdList(pathIdList.stream().distinct().collect(Collectors.toList()));
        Map<String,HtPathConfigurationDTO> pathHash = pathList.stream().collect(Collectors.toMap(HtPathConfigurationDTO::getId,dto -> dto,(k1,k2) -> k2));
        dbDmsSchedulerTaskFileRecordService.prepareDataByFileNameList(taskId,fileNameList);
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        if (metadataDTOList != null) {
            for (HtScanningMetadataDTO dto : metadataDTOList) {
                threadPool.execute(() -> {
                    String fileName = dto.getFileName();
                    dbDmsSchedulerTaskFileRecordService.dataRepeatExecution(taskId,fileName);
                    if (pathHash.containsKey(dto.getPathConfigId())) {
                        HtPathConfigurationDTO pathDTO = pathHash.get(dto.getId());
                        if (dto.getDataLevel().contains("L2")) {
                            String filePathPrefix = pathDTO.getOutputPath();
                            String filePath = filePathPrefix + File.separator + dto.getFilePath();
                            delFile(filePath);
                        }
                        htScanningMetadataService.deleteOne(dto);
                        dbDmsSchedulerTaskFileRecordService.dataRepeatResults(taskId,fileName,true);
                    } else {
                        dbDmsSchedulerTaskFileRecordService.dataRepeatResults(taskId,fileName,false);
                    }
                });
            }
        }
        if (resultDataDTOList != null) {
            for (DbDmsAlgorithmResultStoreDTO dto : resultDataDTOList) {
                threadPool.execute(() -> {
                    String fileName = dto.getFilename();
                    dbDmsSchedulerTaskFileRecordService.dataRepeatExecution(taskId,fileName);
                    if (pathHash.containsKey(dto.getPathConfigId())) {
                        HtPathConfigurationDTO pathDTO = pathHash.get(dto.getId());
                        String filePath = pathDTO.getOutputPath() + File.separator + dto.getFilepath();
                        delFile(filePath);
                        dbDmsAlgorithmResultStoreService.deleteOne(dto);
                        dbDmsSchedulerTaskFileRecordService.dataRepeatResults(taskId,fileName,true);
                    } else {
                        dbDmsSchedulerTaskFileRecordService.dataRepeatResults(taskId,fileName,false);
                    }
                });
            }
        }
        if (nearlineDataDTOList != null) {
            for (DbDmsDatafileNearlineStorageDTO dto : nearlineDataDTOList) {
                threadPool.execute(() -> {
                    String fileName = dto.getFileName();
                    dbDmsSchedulerTaskFileRecordService.dataRepeatExecution(taskId,fileName);
                    if (pathHash.containsKey(dto.getPathConfigId())) {
                        HtPathConfigurationDTO pathDTO = pathHash.get(dto.getId());
                        if (dto.getDataLevel().contains("L2")) {
                            String filePath = pathDTO.getOutputPath() + File.separator + dto.getFilePath();
                            delFile(filePath);
                        }
                        dbDmsDatafileNearlineStorageService.deleteOne(dto);
                        dbDmsSchedulerTaskFileRecordService.dataRepeatResults(taskId,fileName,true);
                    } else {
                        dbDmsSchedulerTaskFileRecordService.dataRepeatResults(taskId,fileName,false);
                    }
                });
            }
        }
        threadPool.shutdown();
        logger.info("[数据清理]数据清理任务完毕");
    }

    public void deleteDistFile(DataClearParams params, String scanPath, String taskId) {
        //查找到需要删除的文件路径集合
        List<File> deleteFileList = new ArrayList<>();
        recurseFile(scanPath, deleteFileList, params);
        //执行删除方法
        delAllFiles(deleteFileList,taskId);
        //删除文件后判断上层文件夹是否需要删除，将删除文件路径集合截取出文件夹路径，并去重之后判断文件夹下是否存在文件，如果不存在则删除
        List<String> dirList = deleteFileList.stream().map(file -> {
            String filePath = file.getPath();
            return filePath.substring(0, filePath.lastIndexOf("/"));
        }).distinct().collect(Collectors.toList());
        dirList.forEach(dir -> delDir(dir,scanPath));
    }

    /**
     * 递归文件夹，根据规则条件，将要删除的文件放入集合中
     *
     * @param path       扫描路径
     * @param deleteList 删除文件的集合
     * @param params     规则条件参数
     */
    private void recurseFile(String path, List<File> deleteList, DataClearParams params) {
        logger.info("[数据清理]当前路径{}",path);
        File file = new File(path);
        if (!file.exists()) {
            return;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                recurseFile(f.getPath(), deleteList, params);
            }
        } else {
            if (checkRule(file.getName(), params)) {
                deleteList.add(file);
            }
        }
    }

    public static void main(String[] args) {
        String fileName = "JB20-1_VNIR_20210121_040024_B04_E129.26_N40.67_M01_GEO_L1B.HDF";
        DataClearParams params = new DataClearParams();
        params.setDateFormat("yyyyMMdd_HHmmss");
        params.setDeleteDb(false);
        params.setDelTime("240");
        params.setFileDatePattern("[0-9]{8}_[0-9]{6}");
        params.setFileNamePattern("[\\s\\S]*");
        boolean b = checkRule(fileName, params);
        System.out.println(b);
    }

    /**
     * 校验文件名称和匹配规则
     *
     * @param fileName
     * @param params
     * @return
     */
    private static boolean checkRule(String fileName, DataClearParams params) {
        String fileNamePattern = params.getFileNamePattern();
        String fileDatePattern = params.getFileDatePattern();
        String dateFormat = params.getDateFormat();
        //delTime 几小时前
        String delTime = params.getDelTime();
        logger.info("[数据清理]fileNamePattern:{},fileDatePattern:{},dateFormat:{},delTime:{}",
                fileNamePattern,fileDatePattern,dateFormat,delTime);
        if (StringUtils.isBlank(fileNamePattern) || StringUtils.isBlank(fileDatePattern)
                || StringUtils.isBlank(dateFormat) || StringUtils.isBlank(delTime)) {
            return false;
        }
        Pattern pattern = Pattern.compile(fileNamePattern);
        Matcher matcher = pattern.matcher(fileName);
        if (matcher.matches()) {
            Pattern p = Pattern.compile(fileDatePattern);
            Matcher m = p.matcher(fileName);
            if (m.find()) {
                String dateTime = m.group();
                try {
                    //文件时间时间戳
                    long milli = LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern(dateFormat))
                            .atZone(ZoneId.systemDefault()).toEpochSecond();
                    //delTime时间戳
                    long plus = Long.valueOf(delTime) * (-1);
                    long checkMilli = LocalDateTime.now().plusHours(plus).atZone(ZoneId.systemDefault()).toEpochSecond();
                    if (milli <= checkMilli) {
                        return true;
                    }
                } catch (Exception e) {
                    logger.error("[数据清理]{}校验时间异常{}",fileName,e.getMessage());
                    //如果不满足该时间转换格式也不会将其删除
                    return false;
                }
            }
        }
        return false;
    }

    /**
     * 删除文件列表中的文件 如果是单纯的文件 会有记录监控相关操作，如果是文件夹列表，则直接删除
     *
     * @param list
     * @param taskId
     */
    private void delAllFiles(List<File> list, String taskId) {
        for (File file : list) {
            if (file.isFile()) {
                dbDmsSchedulerTaskFileRecordService.dataExecution(taskId, file.getName());
                if (file.exists()) {
                    String name = file.getName();
                    if (file.exists()) {
                        file.delete();
                    }
                    dbDmsSchedulerTaskFileRecordService.dataResults(taskId, name, true);
                } else {
                    dbDmsSchedulerTaskFileRecordService.dataResults(taskId, file.getName(), false);
                }
            }
        }
    }

    /**
     * 递归删除目录
     */
    private void delDir(String dirPath, String sourceScanPath) {
        File file = new File(dirPath);
        if (file.exists() && file.isDirectory()) {
            file.delete();
            String preDirPath = dirPath.substring(0,dirPath.lastIndexOf("/"));
            if (!preDirPath.equals(sourceScanPath)) {
                delDir(preDirPath, sourceScanPath);
            }
        }
    }

    private void delFile(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
        }
    }
}
